package van.planifolia.dynamicproxy;

import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import van.planifolia.util.MakeLog;

import java.lang.reflect.Method;

/**
 * Created by Intellij IDEA<br>
 *
 * @author Planifolia.Van
 * @version 1.0
 * @date 2022/11/9 18:25
 */
public class CglibProxy implements MethodInterceptor {
    // 首先还是引入被代理类我们后面用得到
    private Computer computer;

    public CglibProxy(Computer computer) {
        this.computer = computer;
    }

    /**
     * 获取代理对象的方法
     * @return 创建的代理对象
     */
    public Computer getProxyBean(){
        // 首先创建工具对象
        Enhancer enhancer = new Enhancer();
        // 然后调用他的方法来指定被代理类的反射对象
        enhancer.setSuperclass(computer.getClass());
        /*
         指定回调类对象，我们这里填this，这里我们想一下,为什么他需要的是Callback类型的对象我们传入this却好使？this指代的是
         当前对象，也就是CglibProxy这个对象，那么显然了CglibProxy和Callback之间有亲属关系我们一步步点上去可以看到，CglibProxy实现了
         MethodInterceptor，MethodInterceptor又继承了Callback这就很显然了。
         */
        enhancer.setCallback(this);
        // 最后返回代理对象的实例,这也我们就吧代理对象的生产给结局了剩下就是设置方法的增强了
        return (Computer) enhancer.create();
    }

    /**
     * 这个方法会在我们调用被代理类的方法时自动回调执行，显然根据参数我们可以知道method就是当前被调用的方法我们要在这方法前后
     * 实现增强，并且这个方法的参数看着非常唬人，但是我们操心的只有两个 ，objects方法的对应参数，method当前被回调的方法，
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // 方法前增强
        check(method,objects);
        // 方法执行
        Integer result = (Integer) method.invoke(computer, objects);
        // 方法后增强
        doLog(method,objects,result);
        return result;

    }
    /**
     * 计算前减产
     *
     * @param method 被检查的方法对象
     * @param args   被检查的方法参数
     */
    private void check(Method method, Object[] args) {
        if (method.getName().equals("divide")) {
            Integer integer = (Integer) args[1];
            if (integer == 0) {
                throw new RuntimeException("被除数不得为0");
            }
        }
        System.out.println("校验通过");
    }

    /**
     * 打日志的方法
     *
     * @param method 当前的方法
     * @param args   当前方法的参数组
     * @param result 当前方法的返回值
     */
    private void doLog(Method method, Object[] args, Integer result) {
        String name = method.getName();
        Integer t1 = (Integer) args[0];
        Integer t2 = (Integer) args[1];
        MakeLog.makeLog(t1 + " " + name + " " + t2 + " = " + result);
    }
}
